/* @flow */
// 警告/错误处理;标签、attr是否为平台保留字段;是否为异步处理;忽略某些自定义元素;必须使用Prop的属性；
import config from 'core/config'
// warn处理警告的方式
import { warn, cached } from 'core/util/index'
//用来处理性能。mark 记录从某一时刻到记录时刻间的毫秒数。measure清除时刻标记
import { mark, measure } from 'core/util/perf'
// 载入Vue构造函数
import Vue from './runtime/index'
// 查询元素选择器是否已经存在。已经存在直接返回已经存在的元素选择器，否则创建一个
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'

const idToTemplate = cached(id => {
    const el = query(id)
    return el && el.innerHTML
})

// 保留vue实例的 $mount 方法。目的需要重写$mount方法。
const mount = Vue.prototype.$mount
// $mount 把生成的dom挂载到页面上
Vue.prototype.$mount = function (el?: string | Element, hydrating?: boolean): Component {
    // el是创建vue实例时传入的选项
    el = el && query(el)

    /* istanbul ignore if */
    // el不能是body或者html
    if (el === document.body || el === document.documentElement) {
        process.env.NODE_ENV !== 'production' &&
            warn(`Do not mount Vue to <html> or <body> - mount to normal elements instead.`)
        // 如果el 为body或html，提示警告并直接返回vue实例
        return this
    }
    // 获取options选项
    const options = this.$options
    // resolve template/el and convert to render function
    // 把template/el 转换成rende函数
    if (!options.render) {
        let template = options.template
        if (template) {
            if (typeof template === 'string') {
                if (template.charAt(0) === '#') {
                    template = idToTemplate(template)
                    /* istanbul ignore if */
                    if (process.env.NODE_ENV !== 'production' && !template) {
                        warn(`Template element not found or is empty: ${options.template}`, this)
                    }
                }
            } else if (template.nodeType) {
                template = template.innerHTML
            } else {
                if (process.env.NODE_ENV !== 'production') {
                    warn('invalid template option:' + template, this)
                }
                return this
            }
        } else if (el) {
            template = getOuterHTML(el)
        }
        if (template) {
            /* istanbul ignore if */
            if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
                mark('compile')
            }
            // compileToFunctions：把template编译成了render函数
            // staticRenderFns：数组
            const { render, staticRenderFns } = compileToFunctions(
                template,
                {
                    outputSourceRange: process.env.NODE_ENV !== 'production',
                    shouldDecodeNewlines,
                    shouldDecodeNewlinesForHref,
                    delimiters: options.delimiters,
                    comments: options.comments
                },
                this
            )
            // 将生成的render、staticRenderFns记录到vue的options数组中
            options.render = render
            options.staticRenderFns = staticRenderFns

            /* istanbul ignore if */
            if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
                mark('compile end')
                measure(`vue ${this._name} compile`, 'compile', 'compile end')
            }
        }
    }
    // mount 方法渲染dom
    return mount.call(this, el, hydrating)
}

/**
 * Get outerHTML of elements, taking care
 * of SVG elements in IE as well.
 */
function getOuterHTML(el: Element): string {
    if (el.outerHTML) {
        return el.outerHTML
    } else {
        const container = document.createElement('div')
        container.appendChild(el.cloneNode(true))
        return container.innerHTML
    }
}
//Vue增加静态方法，编译，将html字符串编译为render函数
Vue.compile = compileToFunctions

export default Vue
